use crate::AppState;
use ane_system::{
    dao::{DeptListVo, RoleSimpletVo, UserDetailVo, UserVo},
    entity::sys_dept,
};
use axum::{
    async_trait,
    extract::FromRequestParts,
    http::{request, StatusCode},
};
use loginmanager::{AuthUser, UserMinix};
use sea_orm::{
    prelude::Expr, Condition, ConnectionTrait, DbErr, EntityTrait, PaginatorTrait, QueryFilter,
};
use sea_orm::{sea_query::Cond, ColumnTrait};
use serde::{Deserialize, Serialize};

use super::Security;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthRole {
    pub id: i32,
    pub key: String,
    /** 数据范围（1：所有数据权限；2：自定义数据权限；3：本部门及以下数据权限；4：本部门数据权限；5：仅本人数据权限） */
    pub scope: i8,
}

impl AuthRole {
    pub fn new(id: i32, key: String, scope: i8) -> Self {
        Self { id, key, scope }
    }
}

impl From<RoleSimpletVo> for AuthRole {
    fn from(value: RoleSimpletVo) -> Self {
        Self {
            id: value.id,
            key: value.key,
            scope: value.data_scope,
        }
    }
}

/// 当前登陆的用户
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoginUser {
    /// 用户id
    pub id: i32,
    /// 用户所属的单位id
    pub d_id: i32,
    /// 账户名称
    pub name: String,
    /// 用户昵称
    pub nickname: String,
    /// 用户的拥有的角色
    pub roles: Vec<AuthRole>,
    pub status: bool,
}

#[async_trait]
impl UserMinix<axum::http::request::Parts> for LoginUser {
    type Key = i32;

    async fn get_user(id: &Self::Key, req: &mut axum::http::request::Parts) -> Option<Self> {
        let state = req.extensions.get::<AppState>().unwrap();
        Security::load_user(state, id).await
    }

    fn get_id(&self) -> &Self::Key {
        &self.id
    }

    fn is_actived(&self) -> bool {
        self.status
    }
}

#[async_trait]
impl<S> FromRequestParts<S> for LoginUser
where
    S: Send + Sync,
{
    type Rejection = (StatusCode, &'static str);

    async fn from_request_parts(
        parts: &mut axum::http::request::Parts,
        state: &S,
    ) -> Result<Self, Self::Rejection> {
        let u = AuthUser::<Self>::from_request_parts(parts, state).await?.0;
        Ok(u)
    }
}

impl LoginUser {
    /** 数据范围（1：所有数据权限；2：自定义数据权限；3：本部门及以下数据权限；4：本部门数据权限；5：仅本人数据权限） */
    pub fn data_scope(&self) -> i8 {
        let mut scope = 5;
        for r in &self.roles {
            scope = scope.min(r.scope);
        }
        scope
    }

    pub fn dept_cond(&self) -> Cond {
        match self.data_scope() {
            1 => Cond::any().add(Expr::value(1)),
            3 => Cond::any()
                .add(sys_dept::Column::DeptId.eq(self.d_id))
                .add(sys_dept::Column::Ancestors.contains(format!("/{}/", self.d_id))),
            4 => Cond::any().add(sys_dept::Column::DeptId.eq(self.d_id)),
            5 => Cond::all().add(sys_dept::Column::DeptId.eq(self.d_id)),
            _ => Cond::any().add(Expr::value(0)),
        }
    }

    pub fn get_role(&self, name: &str) -> Option<&AuthRole> {
        for r in &self.roles {
            if r.key == name {
                return Some(r);
            }
        }
        None
    }

    #[inline]
    pub fn has_role(&self, name: &str) -> bool {
        self.get_role(name).is_some()
    }

    pub async fn can_access_user_id(
        &self,
        db: &impl ConnectionTrait,
        user_id: i32,
    ) -> Result<bool, sea_orm::DbErr> {
        if self.id == user_id {
            return Ok(true);
        }
        Ok(match self.data_scope() {
            1 => true,
            3 | 4 => {
                ane_system::entity::sys_user::Entity::find_by_id(user_id)
                    .left_join(ane_system::entity::sys_dept::Entity)
                    .filter(ane_system::entity::sys_user::Column::DelFlag.eq(false))
                    .filter(self.dept_cond())
                    .count(db)
                    .await?
                    > 0
            }
            _ => false,
        })
    }

    pub async fn can_access_dept_id(
        &self,
        db: &impl ConnectionTrait,
        dept_id: i32,
    ) -> Result<bool, sea_orm::DbErr> {
        use ane_system::entity::sys_dept;
        Ok(match self.data_scope() {
            1 => true,
            3 => {
                self.d_id == dept_id
                    || sys_dept::Entity::find_by_id(dept_id)
                        .filter(sys_dept::Column::Ancestors.like(format!("/{}/", self.d_id)))
                        .filter(sys_dept::Column::DelFlag.eq(false))
                        .count(db)
                        .await?
                        > 0
            }
            4 => self.d_id == dept_id,
            _ => false,
        })
    }
}

impl From<UserDetailVo> for LoginUser {
    fn from(user: UserDetailVo) -> Self {
        Self {
            id: user.user_id,
            d_id: user.dept_id,
            name: user.username,
            nickname: user.nickname,
            roles: user.roles.into_iter().map(Into::into).collect(),
            status: user.status,
        }
    }
}
